package application;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import transport.Constants;

public class ControlConnectionToTransport extends Thread{
	private int outPort;
	private boolean connected;
	private Socket controlConnection;
	private BufferedReader in;
	private BufferedWriter out;
	private DataConnectionToTransport dataConnection;
	private boolean disconnected;
	private Application application;
	/**
	 * Makes a new ControlConnectionToTransport with the given outPort and application
	 * @param outPort The port it has to send to
	 * @param application The application it connects with
	 * @throws IOException 
	 */
	public ControlConnectionToTransport(int outPort, Application application) throws IOException{
		disconnected = false;
		this.outPort = outPort;
		this.connected = false;
		this.application = application;
		this.controlConnection = new Socket(InetAddress.getByName("localhost"), outPort);
		this.in = new BufferedReader(new InputStreamReader(controlConnection.getInputStream()));
		this.out = new BufferedWriter(new OutputStreamWriter(controlConnection.getOutputStream()));
		this.start();
	}
	/**
	 * Returns true if this class already has a connection to send data, returns false if it hasn't a connection to send data
	 * @return
	 */
	public boolean isConnected(){
		return connected;
	}
	/**
	 * Sends a listen command to the transport layer
	 */
	public void listen(int port){
		send(Constants.LISTEN + " " + port);
	}
	/**
	 * Sends a connect command to the transport layer
	 * @param port
	 * @param tpa
	 * @requires 0 <= port && port <= Constants.PORT_MAX;
	 * @requires 0 <= tpa && tpa <= Network.Constants.TPA_MAX;
	 */
	public void connect(int port, int tpa){
		send(Constants.CONNECTING + " " + port + " " + tpa);
	}
	/**
	 * Handles an incoming connected command, it sets up the dataConnection
	 * @param input
	 * @throws UnknownHostException Exception is localhost is an unknown host
	 * @throws IOException Exception if the transport layer is not listening anymore
	 * @require input.startsWith(Constants.CONNECTED)
	 * @ensure isConnected();
	 */
	public void connected(String input){
		String[] arguments = input.split(" ");
		int port = Integer.parseInt(arguments[1]);
		Socket dataSocket;
		try {
			dataSocket = new Socket(InetAddress.getByName("localhost"), port);
			dataConnection = new DataConnectionToTransport(dataSocket, application);
			application.connected();
			connected = true;
		} catch (UnknownHostException e) {
			//kan niet gebeuren
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		application.connected();
	}
	/**
	 * Sends a disconnect command to the transport layer
	 */
	public void disconnect(){
		send(Constants.DISCONNECT);
		dataConnection.disconnect();
		connected = false;
		disconnected = true;
	}
	/**
	 * Method that is called if a disconnected command has been received
	 */
	public void disconnected(){
		dataConnection.disconnect();
		application.disconnected();
		connected = false;
		disconnected = true;
	}
	/**
	 * Sends a message to the transport layer over the control connection and ends the message with an endline.
	 * @param msg the message to send
	 */
	public void send(String msg){
		try {
			out.write(msg + '\n');
			out.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/**
	 * method that handles an incoming command
	 * @param input
	 */
	public void handleInput(String input){
		if (input.startsWith(Constants.CONNECTED)){
			connected(input);
		} else {
			if (input.startsWith(Constants.DISCONNECTED)){
				disconnected();
			}
		}
	}
	/**
	 * Run method that reads incoming input.
	 */
	public void run(){
		while (!disconnected){
			try {
				String input = in.readLine();
				System.out.println(input);
				handleInput(input);
			} catch (IOException e) {
				disconnected = true;
			}
		}
	}
	/**
	 * Returns the dataconnection that belongs to this ControlConnection
	 * @return The dataconnection
	 */
	public DataConnectionToTransport getDataConnection(){
		return this.dataConnection;
	}
	

}
